#pragma once

#include "BufferKeeper.h"
#include "Booker.h"
#include "SubpixelInfoProvider.h"


enum OverrideToken
{
	kOverrideNone = 0,
	kOverrideOtherStroke,
	kOverrideBeyondPictureBounds
};

class Calculator
{
	struct Info { 
		int xi, eta, xiStraight; 
		float f; 
		const SubpixelInfo* p; 
	};
	typedef Info* InfoPtr;
	typedef const Info* ConstInfoPtr;
	typedef bool (*CompareIntsFunction)(int, int);

	mutable SubpixelInfoProvider mSubpixelInfoProvider;

	Booker mTotalsBooker, mAnchorDotBooker, mDotBooker;
	Booker *mCurrentDotBooker;

	BufferKeeper mBufferKeeper1, mBufferKeeper2;
	InfoPtr mInfos;
	InfoPtr mInfosEnd;
	ConstInfoPtr mInfoWithMaxValue;
	char mOverlapFlags;
	bool mIsEndToEnd;

	int mMinXi, mMaxXi, mMinEta, mMaxEta, mMaxLimit;
	int mInitialDotHalfsizeBig;
	float mDeltaXi, mLineHalfwidth, mCachedVx, mCachedVy, mScaledCurvature;
	float mMaxDotHalfsize, mTooBigSquaredLineWidth;
	float mThresholdFactor;
	int mCurvatureCalculationToken;

	// coefficients of an approximating parabola (f = a*x*x + b*x + c)
	float mParabolaA, mParabolaB, mParabolaC, mParabolaD;
	float mApproximationEpsilon, mSquaredLineWidth;

	bool mRegardEtaVariations;

public:
	Calculator(const IBitmapPicture&, float blurRadius);
	~Calculator();

	const SubpixelInfo* GetInfoBuffer() const;
	const SubpixelInfo* GetInfo(int x, int y) const;

	void SetThresholdFactor(float);
	void SetInitialBounds(float halfsize, float curvature);
	void SetInitialDefaultBounds();
	void ExtendXiBounds();

	bool UpdateCache(float x, float y, float vx, float vy, bool checkOccupied = false);
	bool CalculateF(float a, float b);
	void SummarizeAB(float& outA, float& outB) const;
	void SummarizeABAndCalculateCurvature(float& outA, float& outB, float& outCurvature);

	// the argument shoud point to the array of six integers - 
	// there will be stored the two RGB colors
	void SummarizeSideColors(float xiShift, short int* outColorsComponents);

	void AdjustXiBounds();
	void AdjustRectExceptFront();
	void AdjustRectExceptBack();
	void CutAtTheConcaveXi();

	bool FindAlternativeXiBounds();

	// check alignment of gradient direction in the pixel with maximum,
	// and the coordinates of the reference point 
	// (xi = 0, eta = referencePointEta * kTwiceMagnification).
	bool CheckAlignmentOfMaxPixel(float referencePointEta) const;

	// returns precision of approximation (epsilon)
	bool ApproximateByParabola(bool calculateAll = true);
	float GetLastApproximationEpsilon() const;
	void ResetEtaCalculations();
	bool IsEtaCalculations() const;

	// this method shold be called after the calculation of parabola
	OverrideToken HandleOverrides(float x0, float y0);

	void SetMaximumDotSize(float size);
	void SetCurvatureCalculationToken(int);

	float GetDeltaXi() const;
	float GetLineWidth() const;
	float GetDotHalfsize() const;
	bool IsForwardOverlapping() const;
	bool IsBackwardOverlapping() const;

	void NoteToAnchor();
	void NoteToCurrent();
	void MarkOccupiedTheCurrent();
	void ChangeTheAnchorState(bool occupiedState);
	void UpdateBooking(char savingMask);
	
	bool IsMiddlePoint() const;
	void GetXiBounds(float& outMinXi, float& outMaxXi) const;
	float GetMinEta() const;
	float GetMaxEta() const;

private:
	
	static inline 
	ConstInfoPtr* FilterAndSort(ConstInfoPtr* begin, ConstInfoPtr* end, int xiLimit,
		CompareIntsFunction compareXi, CompareIntsFunction compareEta);

	bool ApproximateByParabola1(bool calculateAll);
	void GetTwiceXYLimit(float v1, float v2, int twiceCoord,
		int& outMin, int& outMax, int& outFirstDelta);
	void NoteToBooker(Booker&);
};

